Implement shaped windows
authorAlexander Larsson <alexl@redhat.com>
Tue, 16 Dec 2008 19:09:20 +0000 (20:09 +0100)
committerAlexander Larsson <alex@localhost.localdomain>
Thu, 2 Apr 2009 08:15:16 +0000 (10:15 +0200)
gdk/gdkinternals.h
gdk/gdkwindow.c
gdk/gdkwindow.h
gdk/x11/gdkwindow-x11.c

index 2766c8dfb772b41a60161fef9f1629956c1028ca..7c119a493f46f3e196401e4b56893737ae898c2f 100644 (file)
@@ -336,6 +336,8 @@ void     _gdk_windowing_window_get_offsets      (GdkWindow  *window,
                                                 gint       *x_offset,
                                                 gint       *y_offset);
 GdkRegion *_gdk_windowing_window_get_shape      (GdkWindow  *window);
+GdkRegion *_gdk_windowing_get_shape_for_mask    (GdkBitmap *mask);
+
 
 void       _gdk_windowing_get_pointer        (GdkDisplay       *display,
                                              GdkScreen       **screen,
index 9655e31c605d1e3924df79e5863bd0b3fd6783a3..6ea81d042d914b15733991af027a003dfbe43ebc 100644 (file)
@@ -457,10 +457,25 @@ remove_child_area (GdkWindowObject *private,
       r.y = child->y;
       r.width = child->width;
       r.height = child->height;
-      
+
       child_region = gdk_region_rectangle (&r);
+      
+      if (child->shape)
+       gdk_region_intersect (child_region, child->shape);
+      else if (private->window_type == GDK_WINDOW_FOREIGN)
+       {
+         GdkRegion *shape;
+         shape = _gdk_windowing_window_get_shape ((GdkWindow *)child);
+         if (shape)
+           {
+             gdk_region_intersect (child_region, shape);
+             gdk_region_destroy (shape);
+           }
+       }
+      
       gdk_region_subtract (region, child_region);
       gdk_region_destroy (child_region);
+
     }
 }
 
@@ -512,7 +527,7 @@ recompute_visible_regions_internal (GdkWindowObject *private,
       r.width = private->width;
       r.height = private->height;
       new_clip = gdk_region_rectangle (&r);
-      
+
       if (private->parent != NULL && GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT)
        {
          gdk_region_intersect (new_clip, private->parent->clip_region);
@@ -524,6 +539,9 @@ recompute_visible_regions_internal (GdkWindowObject *private,
       /* Convert from parent coords to window coords */
       gdk_region_offset (new_clip, -private->x, -private->y);
 
+      if (private->shape)
+       gdk_region_intersect (new_clip, private->shape);
+
       if (private->clip_region == NULL ||
          !gdk_region_equal (private->clip_region, new_clip))
        clip_region_changed = TRUE;
@@ -562,9 +580,10 @@ recompute_visible_regions_internal (GdkWindowObject *private,
       gdk_window_has_impl (private) &&
       /* Not for offscreens */
       private->window_type != GDK_WINDOW_OFFSCREEN &&
-      /* or for toplevels */
-      private->parent != NULL &&
-      GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT &&
+      /* or for non-shaped toplevels */
+      (private->shaped ||
+       (private->parent != NULL &&
+       GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT)) &&
       /* or for foreign windows */
       GDK_WINDOW_TYPE (private) != GDK_WINDOW_FOREIGN
       )
@@ -6171,12 +6190,19 @@ gdk_window_shape_combine_mask (GdkWindow *window,
                                gint       y)
 {
   GdkWindowObject *private;
+  GdkRegion *region;
 
   g_return_if_fail (GDK_IS_WINDOW (window));
 
   private = (GdkWindowObject *) window;
 
-  GDK_WINDOW_IMPL_GET_IFACE (private->impl)->shape_combine_mask (window, mask, x, y);
+  region = _gdk_windowing_get_shape_for_mask (mask);
+
+  gdk_window_shape_combine_region (window,
+                                  region,
+                                  x, y);
+
+  gdk_region_destroy (region);
 }
 
 /**
@@ -6214,7 +6240,47 @@ gdk_window_shape_combine_region (GdkWindow       *window,
 
   private = (GdkWindowObject *) window;
 
-  GDK_WINDOW_IMPL_GET_IFACE (private->impl)->shape_combine_region (window, shape_region, offset_x, offset_y);
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  private->shaped = (shape_region != NULL);
+
+  if (private->shape)
+    gdk_region_destroy (private->shape);
+
+  if (shape_region)
+    {
+      private->shape = gdk_region_copy (shape_region);
+      gdk_region_offset (private->shape, offset_x, offset_y);      
+    }
+  else
+    private->shape = NULL;
+  
+  recompute_visible_regions (private, TRUE, FALSE);
+}
+
+static void
+do_child_shapes (GdkWindow *window,
+                gboolean merge)
+{
+  GdkWindowObject *private;
+  GdkRectangle r;
+  GdkRegion *region;
+
+  private = (GdkWindowObject *) window;
+  
+  r.x = 0;
+  r.y = 0;
+  r.width = private->width;
+  r.height = private->height;
+  
+  region = gdk_region_rectangle (&r);
+  remove_child_area (private, NULL, region);
+
+  if (merge && private->shape)
+    gdk_region_subtract (region, private->shape);
+  
+  gdk_window_shape_combine_region (window, region, 0, 0);
 }
 
 /**
@@ -6229,13 +6295,9 @@ gdk_window_shape_combine_region (GdkWindow       *window,
 void
 gdk_window_set_child_shapes (GdkWindow *window)
 {
-  GdkWindowObject *private;
-
   g_return_if_fail (GDK_IS_WINDOW (window));
 
-  private = (GdkWindowObject *) window;
-
-  GDK_WINDOW_IMPL_GET_IFACE (private->impl)->set_child_shapes (window);
+  do_child_shapes (window, FALSE);
 }
 
 /**
@@ -6254,13 +6316,9 @@ gdk_window_set_child_shapes (GdkWindow *window)
 void
 gdk_window_merge_child_shapes (GdkWindow *window)
 {
-  GdkWindowObject *private;
-
   g_return_if_fail (GDK_IS_WINDOW (window));
 
-  private = (GdkWindowObject *) window;
-
-  GDK_WINDOW_IMPL_GET_IFACE (private->impl)->merge_child_shapes (window);
+  do_child_shapes (window, TRUE);
 }
 
 
@@ -6650,10 +6708,12 @@ static gboolean
 point_in_window (GdkWindowObject *window,
                 double x, double y)
 {
-  /* TODO: Input Shape */
   return
     x >= 0 &&  x < window->width &&
-    y >= 0 && y < window->height;
+    y >= 0 && y < window->height &&
+    (window->shape == NULL ||
+     gdk_region_point_in (window->shape,
+                         x, y));
 }
 
 static void
index 5c475785f0124978e18e258e4eaff58ce007d7c9..bc08cbc51ba15b5f48599f3a3700b5caaac99766 100644 (file)
@@ -337,6 +337,8 @@ struct _GdkWindowObject
   GdkWindowPaint *implicit_paint;
 
   GList *outstanding_moves;
+
+  GdkRegion *shape;
   
   cairo_surface_t *cairo_surface;
 };
index 2b00d9582f4098229da2f6d4140580d96467eef0..85f81e5135e60cb44f2d0048d599f6e5e085af33 100644 (file)
@@ -4757,48 +4757,92 @@ gdk_add_to_span (struct _gdk_span **s,
   return;
 }
 
-GdkRegion *
-_gdk_windowing_window_get_shape (GdkWindow *window)
+static GdkRegion *
+xwindow_get_shape (Display *xdisplay,
+                  Window window)
 {
   GdkRegion *shape;
+  GdkRectangle *rl;
+  XRectangle *xrl;
+  gint rn, ord, i;
 
   shape = NULL;
   
 #if defined(HAVE_SHAPE_EXT)
-  if (!GDK_WINDOW_DESTROYED (window) &&
-      gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window)))
+  xrl = XShapeGetRectangles (xdisplay,
+                            window,
+                            ShapeBounding, &rn, &ord);
+
+  if (rn == 0)
+    return NULL;
+  
+  if (ord != YXBanded)
     {
-      GdkRectangle *rl;
-      XRectangle *xrl;
-      gint rn, ord, i;
-      
-      xrl = XShapeGetRectangles (GDK_WINDOW_XDISPLAY (window),
-                                GDK_WINDOW_XID (window),
-                                ShapeBounding, &rn, &ord);
-      
-      if (ord != YXBanded)
-       {
-         /* This really shouldn't happen with any xserver, as they
-            generally convert regions to YXBanded internally */
-         g_warning ("non YXBanded shape masks not supported");
-         XFree (rl);
-         return NULL;
-       }
+      /* This really shouldn't happen with any xserver, as they
+        generally convert regions to YXBanded internally */
+      g_warning ("non YXBanded shape masks not supported");
+      XFree (xrl);
+      return NULL;
+    }
 
-      rl = g_new (GdkRectangle, rn);
-      for (i = 0; i < rn; i++)
-       {
-         rl[i].x = xrl[i].x;
-         rl[i].y = xrl[i].y;
-         rl[i].width = xrl[i].width;
-         rl[i].height = xrl[i].height;
-       }
-      
-      shape = _gdk_region_new_from_yxbanded_rects (rl, rn);
-      g_free (rl);
-      XFree (rl);
+  rl = g_new (GdkRectangle, rn);
+  for (i = 0; i < rn; i++)
+    {
+      rl[i].x = xrl[i].x;
+      rl[i].y = xrl[i].y;
+      rl[i].width = xrl[i].width;
+      rl[i].height = xrl[i].height;
     }
+  XFree (xrl);
+  
+  shape = _gdk_region_new_from_yxbanded_rects (rl, rn);
+  g_free (rl);
+#endif
+  
+  return shape;
+}
+
+
+GdkRegion *
+_gdk_windowing_get_shape_for_mask (GdkBitmap *mask)
+{
+  GdkDisplay *display;
+  Window window;
+  GdkRegion *region;
+
+  display = gdk_drawable_get_display (GDK_DRAWABLE (mask));
+
+  window = XCreateSimpleWindow (GDK_DISPLAY_XDISPLAY (display),
+                               GDK_SCREEN_XROOTWIN (gdk_display_get_default_screen (display)),
+                               -1, -1, 1, 1, 0,
+                               0, 0);
+  XShapeCombineMask (GDK_DISPLAY_XDISPLAY (display),
+                    window,
+                    ShapeBounding,
+                    0, 0,
+                    GDK_PIXMAP_XID (mask),
+                    ShapeSet);
+  
+  region = xwindow_get_shape (GDK_DISPLAY_XDISPLAY (display),
+                             window);
+
+  XDestroyWindow (GDK_DISPLAY_XDISPLAY (display),
+                 window);
+
+  return region;
+}
+
+GdkRegion *
+_gdk_windowing_window_get_shape (GdkWindow *window)
+{
+#if defined(HAVE_SHAPE_EXT)
+  if (!GDK_WINDOW_DESTROYED (window) &&
+      gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window)))
+    return xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
+                             GDK_WINDOW_XID (window));
 #endif
+
+  return NULL;
 }
 
 static void